﻿#include "uscorewidget.h"
#include "uapplication.h"
#include "style/upalette.h"
#include "ulabel.h"

#include <QPainter>
#include <QPaintEvent>
#include <QMouseEvent>
#include <QPainterPath>
#include <QtMath>
#include <QHBoxLayout>

class UFivePointedStarPrivate
{
public:
    UFivePointedStarPrivate(UFivePointedStar* q) : q_ptr(q) {}
    ~UFivePointedStarPrivate() {}

    void initStarData(const QPoint &center);

private:
    Q_DECLARE_PUBLIC(UFivePointedStar)
    Q_DISABLE_COPY(UFivePointedStarPrivate)
    UFivePointedStar* const q_ptr = nullptr;

    QPoint starData[10];
    double percentage = 1.0;
    double angle = 0.0;
};

void UFivePointedStarPrivate::initStarData(const QPoint &center)
{
    qreal angle = ((360 / 5) * M_PI) / 180;
    int r = center.x();
    starData[0] = QPoint(center.x() + r * qSin(angle), center.y() - r * qCos(angle));
    starData[1] = QPoint(center.x() + (center.y() - starData[0].y()) * qSin(angle / 2), starData[0].y());
    starData[2] = QPoint(r, 0);
    starData[3] = QPoint(center.x() - (center.y() - starData[0].y()) * qSin(angle / 2), starData[0].y());
    starData[4] = QPoint(center.x() - r * qSin(angle), center.y() - r * qCos(angle));

    int width = qAbs(starData[1].x() - starData[3].x());
    starData[5] = QPoint(starData[3].x() - width * qCos(angle), starData[0].y() + width * qSin(angle));
    starData[6] = QPoint(center.x() - r * qSin(angle / 2), center.y() + r * qCos(angle / 2));
    starData[7] = QPoint(center.x(), starData[0].y() + (center.x() - starData[4].x()) * qTan(angle / 2));
    starData[8] = QPoint(center.x() + r * qSin(angle / 2), center.y() + r * qCos(angle / 2));
    starData[9] = QPoint(starData[1].x() + width * qCos(angle), starData[0].y() + width * qSin(angle));
}

UFivePointedStar::UFivePointedStar(QWidget* parent) : QWidget(parent)
    , d_ptr(new UFivePointedStarPrivate(this))
{
    Q_D(UFivePointedStar);
    d->percentage = 1.0;
}

UFivePointedStar::UFivePointedStar(double percentage, QWidget* parent) : QWidget(parent)
    , d_ptr(new UFivePointedStarPrivate(this))
{
    Q_D(UFivePointedStar);
    if (percentage > 1.0)
    {
        percentage = 1.0;
    }
    if (percentage < 0.0)
    {
        percentage = 0.0;
    }
    d->percentage = percentage;
}

UFivePointedStar::~UFivePointedStar()
{
}

void UFivePointedStar::setPercentage(double percentage)
{
    Q_D(UFivePointedStar);
    if (percentage > 1.0)
    {
        percentage = 1.0;
    }
    if (percentage < 0.0)
    {
        percentage = 0.0;
    }
    d->percentage = percentage;
    update();
}

double UFivePointedStar::percentage() const
{
    Q_D(const UFivePointedStar);
    return d->percentage;
}

void UFivePointedStar::rotate(double angle)
{
    Q_D(UFivePointedStar);
    if (d->angle != angle)
    {
        d->angle = angle;
        update();
    }
}

void UFivePointedStar::mousePressEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        emit mousePressed();
    }
    QWidget::mousePressEvent(event);
}

void UFivePointedStar::mouseReleaseEvent(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        emit mouseReleased();
        emit mouseClicked();
    }
    QWidget::mouseReleaseEvent(event);
}

void UFivePointedStar::paintEvent(QPaintEvent* event)
{
    Q_D(UFivePointedStar);

    int r = qMin(this->width(), this->height());
    QRect rect = QRect(0, 0, r, r);
    d->initStarData(rect.center());

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setPen(QPen(uApp->applicationPalette()->color(QPalette::Button), 2));

    QPainterPath paintPath;
    paintPath.moveTo(d->starData[0]);
    for (int i = 1; i < 10; ++i)
    {
        paintPath.lineTo(d->starData[i]);
    }
    paintPath.closeSubpath();

    QPainterPath rectPath;
    rect.setWidth(rect.width() * d->percentage);
    rectPath.addRect(rect);

    painter.translate(rect.center());
    painter.rotate(d->angle);
    painter.translate(-rect.center());

    painter.setBrush(Qt::NoBrush);
    painter.drawPath(paintPath);

    painter.setBrush(uApp->applicationPalette()->color(UPalette::WarningBackground));
    auto intersectedPath = paintPath.intersected(rectPath);
    painter.drawPath(intersectedPath);
}

class UScoreWidgetPrivate
{
public:
    UScoreWidgetPrivate(UScoreWidget* q) : q_ptr(q) {}
    ~UScoreWidgetPrivate() {}

    void init(int num);

    void resetStars();

private:
    Q_DECLARE_PUBLIC(UScoreWidget)
    Q_DISABLE_COPY(UScoreWidgetPrivate)
    UScoreWidget* const q_ptr = nullptr;

    int count = 5;
    QList<UFivePointedStar*> starList;
    ULabel* label = nullptr;
    QHBoxLayout* mainLayout = nullptr;
};

void UScoreWidgetPrivate::init(int num)
{
    Q_Q(UScoreWidget);
    if (num < 1)
    {
        num = 1;
    }
    count = num;
    mainLayout = new QHBoxLayout;
    label = new ULabel("0.0", q);
    label->setTextSize(14);
    label->setFontWeight(QFont::Bold);
    q->setLayout(mainLayout);
    for (int i = 0; i < num; ++i)
    {
        UFivePointedStar* star = new UFivePointedStar(0.0, q);
        q->connect(star, &UFivePointedStar::mouseClicked, q, &UScoreWidget::onMouseClicked);
        mainLayout->addWidget(star);
        starList.append(star);
    }
    mainLayout->addWidget(label);
}

void UScoreWidgetPrivate::resetStars()
{
    if (!starList.isEmpty())
    {
        const QList<UFivePointedStar*> labelListCopy = starList;
        starList.clear();
        for (int i = labelListCopy.count() - 1; i >= 0; --i)
        {
            UFivePointedStar* animation = labelListCopy.at(i);
            mainLayout->removeWidget(animation);
            animation->setParent(nullptr);
            delete animation;
        }
        count = 0;
        label->setText("0.0");
    }
}

UScoreWidget::UScoreWidget(QWidget* parent) : QWidget(parent)
    , d_ptr(new UScoreWidgetPrivate(this))
{
    Q_D(UScoreWidget);
    d->init(5);
}

UScoreWidget::UScoreWidget(int count, QWidget* parent) : QWidget(parent)
    , d_ptr(new UScoreWidgetPrivate(this))
{
    Q_D(UScoreWidget);
    d->init(count);
}

UScoreWidget::~UScoreWidget()
{
}

void UScoreWidget::setLabelVisible(bool isVisible)
{
    Q_D(UScoreWidget);
    d->label->setVisible(isVisible);
}

void UScoreWidget::setScoreValue(double value)
{
    Q_D(UScoreWidget);
    if (value < 0.0)
    {
        qWarning() << QString("ScoreValue is invalid, value = %1").arg(value);
        return;
    }

    if (value > d->count)
    {
        qWarning() << QString("ScoreValue is invalid, max count = %1").arg(d->count);
        return;
    }

    int count = value;
    double last = value - count;
    for (int i = 0; i < d->starList.size(); ++i)
    {
        if (i <= (count - 1))
        {
            d->starList[i]->setPercentage(1.0);
        }
        else if (i == count)
        {
            d->starList[i]->setPercentage(last);
        }
        else
        {
            d->starList[i]->setPercentage(0.0);
        }
    }
    d->label->setText(QString::number(value, 'f', 1));
}

void UScoreWidget::setCount(int count)
{
    Q_D(UScoreWidget);
    if (count < 1)
    {
        qWarning() << QString("Count must be greater than or equal to 1");
        return;
    }
    if (d->count != count)
    {
        d->resetStars();
        d->count = count;
        for (int i = 0; i < d->count; ++i)
        {
            UFivePointedStar* star = new UFivePointedStar(0.0, this);
            connect(star, &UFivePointedStar::mouseClicked, this, &UScoreWidget::onMouseClicked);
            d->mainLayout->insertWidget(i, star);
            d->starList.append(star);
        }
    }
}

int UScoreWidget::count() const
{
    Q_D(const UScoreWidget);
    return d->count;
}

void UScoreWidget::onMouseClicked()
{
    Q_D(UScoreWidget);
    UFivePointedStar* star = qobject_cast<UFivePointedStar*>(sender());
    if (nullptr == star || d->starList.isEmpty())
    {
        return;
    }

    int index = d->starList.indexOf(star);
    if (0 > index)
    {
        return;
    }

    for (int i = 0; i < d->starList.size(); ++i)
    {
        if (i <= index)
        {
            d->starList[i]->setPercentage(1.0);
        }
        else
        {
            d->starList[i]->setPercentage(0.0);
        }
    }
    d->label->setText(QString::number(index + 1, 'f', 1));
}
